/**
 * Copyright 2008 Adam Ruggles.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package kiff.util.model;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.Reader;
import java.io.StreamTokenizer;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;
import java.util.Stack;

import kiff.util.parser.BBCodeTokenizer;
import kiff.util.parser.StringTokenizer;
import kiff.util.parser.Type;

/**
 * Default Implementation of the parser.
 * @author Adam
 * @version $Id$
 * 
 * Created on Dec 4, 2008 at 8:16:16 PM 
 */
public class DefaultParser {

	/**
	 * The token stack.
	 */
	private Stack<Token> stack = new Stack<Token>();
	/**
	 * The current state.
	 */
	private int state;

	/**
	 * 
	 * @param data
	 * @throws IOException
	 */
	public void lex(final String data) throws IOException {
		BBCodeTokenizer lex = new BBCodeTokenizer(new StringReader(data));
		boolean done = false;
		while (!done) {
			kiff.util.parser.Token tok = lex.nextToken();
			if (tok.getType() == Type.EOF) {
				done = true;
			}
			System.out.println("<<" + tok + ", " + tok.getType() + ">>");
		}
	}

	/**
	 * 
	 * @param data
	 * @throws IOException
	 */
	public void lex2(final String data) throws IOException {
		StringTokenizer lex = new StringTokenizer(data);
		boolean done = false;
		while (!done) {
			kiff.util.parser.Token tok = lex.nextToken();
			if (tok.getType() == Type.EOF) {
				done = true;
			}
			System.out.println("<<" + tok + ", " + tok.getType() + ">>");
		}
	}

	/**
	 * 
	 * @param iteration
	 * @param node
	 * @return
	 */
	private String printTree(final int iteration, final TreeNode node) {
		StringBuilder buff = new StringBuilder();
		for (int i = 0; i < iteration; i++) {
			buff.append(" ---> ");
		}
		if (node.isLeaf()) {
			buff.append(node.getText()).append('\n');
		} else {
			buff.append(node.getText()).append('\n');
			for (TreeNode child : node.getChildren()) {
				buff.append(printTree((iteration + 1), child));
			}
			for (int i = 0; i < iteration; i++) {
				buff.append(" ---> ");
			}
			buff.append(node.getText()).append('\n');
		}
		return buff.toString();
	}

	/**
	 * Parses the input data.
	 * @param data The input data.
	 * @param delimiters A Set of delimiters used as action points.
	 * @throws IOException If an error occurs parsing the string.
	 * TODO remove the IOException.
	 */
	public void parse(final String data, final Set<Integer> delimiters) throws IOException {
		TreeNode node = new DefaultTreeNode("root", 0);
		
		int read = 0;
		Reader reader = new BufferedReader(new StringReader(data));
		StringBuilder sb = new StringBuilder();
		while ((read = reader.read()) != -1) {
			if (delimiters.contains(read)) {
				switch (read) {
				case '[':
					if (sb.length() > 0) {
						node.addChild(new DefaultTreeNode(node, sb.toString(), 0));
						sb.setLength(0); // Clear the buffer.
					}
					state = 1;
					break;
				case ']':
					if (state == 1 && sb.length() > 0) {
						TreeNode child = new DefaultTreeNode(node, sb.toString(), 1);
						node.addChild(child);
						node = child;
						sb.setLength(0);
					} else if (state == 2 && node.getParent() != null) {
						node = node.getParent();
						sb.setLength(0);
					} else if (state == 3) {
						node.addChild(new DefaultTreeNode(node, sb.toString(), 3));
						sb.setLength(0);
					} else {
						sb.append((char) read);
					}
					state = 0;
					break;
				case '/':
					if (state == 1) {
						state = 2;
					} else {
						sb.append((char) read);
					}
					break;
				case '=':
					if (state == 1) {
						state = 3;
						TreeNode child = new DefaultTreeNode(node, sb.toString(), 1);
						node.addChild(child);
						node = child;
						sb.setLength(0);
					}
					break;
				case ' ':
					sb.append((char) read);
					break;
				default:
				}
				
			} else {
				sb.append(String.valueOf((char) read));
			}
		}
		while (node.getParent() != null) {
			node = node.getParent();
		}
		System.out.println(printTree(0, node));
		System.out.println("-----------------------------------");
		System.out.println(node.toString());
		System.out.println("-----------------------------------");
		reader = new BufferedReader(new StringReader(data));
		StreamTokenizer st = new StreamTokenizer(reader);
		st.ordinaryChar('/');
		while (st.nextToken() != StreamTokenizer.TT_EOF) {
			switch(st.ttype) {
			case StreamTokenizer.TT_WORD:
				System.out.println(st.sval);
				break;
			case StreamTokenizer.TT_NUMBER:
				System.out.println(Double.toString(st.nval));
				break;
			case StreamTokenizer.TT_EOL:
				System.out.println("EOL");
	            break;
			default:
				System.out.println(String.valueOf((char) st.ttype));
			}
		}
	}
	/**
	 * @param args
	 * @throws IOException 
	 */
	public static void main(final String[] args) throws IOException {
		DefaultParser parser = new DefaultParser();
		String data = "A [hello This is a test \n[quote=adam ruggles][b]test[/b][/quote]omega" + ((char) 956);
		Set<Integer> delmimiters = new HashSet<Integer>(new ArrayList<Integer>(Arrays.asList(new Integer[] {
				(int) '[', (int) ']', (int) '/', (int) '=', (int) ' '})));
		parser.parse(data, delmimiters);
		parser.lex(data);
		System.out.println("-----------------------------------\\/");
		parser.lex2(data);
	}

}
